package com.tripleying.dogend.dnfipm.util;

import com.tripleying.dogend.dnfipm.App;
import com.tripleying.dogend.dnfipm.model.NPKItem;
import com.tripleying.dogend.dnfipm.original.*;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.nio.file.Files;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;

/**
 * 文件工具
 * @author Dogend
 */
public class FileUtil {
    
    /**
     * 程序运行时, jar文件所在的目录
     */
    private static final File jarFolder;
    
    static {
        jarFolder = new File(System.getProperties().getProperty("user.dir"));
    }
    
    public static File getFolder(){
        return jarFolder;
    }
    
    public static Parent loadFXML(String fxml) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
        return fxmlLoader.load();
    }
    
    /**
     * 获取配置文件保存的DNF补丁目录
     * @return 目录或null
     */
    public static String getConfig(String key){
        FileInputStream in = null;
        InputStreamReader isr = null;
        try {
            File file = new File(getFolder(), key+".prop");
            if(file.exists()){
                Properties prop = new Properties();
                in = new FileInputStream(file);
                isr = new InputStreamReader(in, "UTF-8");
                prop.load(isr);
                if(prop.containsKey(key)){
                    String path = prop.getProperty(key);
                    if(path.endsWith("/")) path = path.substring(0, path.length()-1);
                    return prop.getProperty(key);
                }
            }
        } catch (Exception ex) {
            System.out.println(ex);
        } finally {
            try {
                if(isr!=null) isr.close();
                if(in!=null) in.close();
            } catch (Exception ex) {}
        }
        return null;
    }
    
    /**
     * 将新的DNF补丁目录保存到配置文件
     * @param path 路径
     * @return 是否成功
     */
    public static boolean saveConfig(String key, String value){
        Properties prop = new Properties();
        prop.setProperty(key, value);
        File file = new File(getFolder(), key+".prop");
        FileOutputStream os = null;
        OutputStreamWriter osw = null;
        try {
            if(!file.exists()){
                if(!file.createNewFile()){
                    return false;
                }
            }
            os = new FileOutputStream(file);
            osw = new OutputStreamWriter(os, "UTF-8");
            prop.store(osw, null);
            osw.close();
            os.close();
            if(Objects.equals(value, getConfig(key))){
                return true;
            }
        } catch (Exception ex) {
            System.out.println(ex);
        } finally {
            try {
                if(osw!=null) osw.close();
                if(os!=null) os.close();
            } catch (Exception ex) {}
        }
        return false;
    }
    
    /**
     * 通过个性化文件名获取其中的补丁列表
     * @param name 文件名
     * @return 补丁名列表
     */
    public static List<String> getCustom(String name){
        List<String> list = new ArrayList();
        File file = new File(getFolder(),"custom");
        file = new File(file, name+".txt");
        if(file.exists()){
            try {
                FileInputStream is = new FileInputStream(file);
                InputStreamReader isr = new InputStreamReader(is, "UTF-8");
                BufferedReader br = new BufferedReader(isr);
                String line = "";
                while ((line = br.readLine()) != null){
                    if(line.trim().length()!=0){
                        list.add(line);
                    }
                }
                br.close();
                isr.close();
                is.close();
            } catch (Exception ex) {
                System.out.println(ex);
            }
        }
        return list;
    }
    
    /**
     * 保存个性化文件
     * @param name 文件名
     * @param list 补丁列表
     * @return 是否成功
     */
    public static boolean saveCustom(String name, List<String> list){
        File file = new File(getFolder(),"custom");
        if(!file.exists() && !file.mkdir()){
            return false;
        }
        file = new File(file, name+".txt");
        try {
            if(!file.exists()){
                if(!file.createNewFile()){
                    return false;
                }
            }
            FileOutputStream os = new FileOutputStream(file);
            OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
            BufferedWriter bw = new BufferedWriter(osw);
            bw.write("");
            bw.flush();
            for(String npk:list){
                bw.append(npk);
                bw.newLine();
                bw.flush();
            }
            bw.close();
            osw.close();
            os.close();
            return true;
        } catch (Exception ex) {
            System.out.println(ex);
            return false;
        }
    }
    
    /**
     * 修改DNFImagePacks2Manager.cfg配置
     * @param version 版本
     * @param jarfile 文件
     * @throws FileNotFoundException
     * @throws UnsupportedEncodingException
     * @throws IOException 
     */
    public static void saveAppConfig(String version, File jarfile) throws FileNotFoundException, UnsupportedEncodingException, IOException{
        File file = new File(jarFolder, "DNFImagePacks2Manager.cfg");
        InputStream in = new FileInputStream(file);
        Reader isr = new InputStreamReader(in, "UTF-8");
        BufferedReader br = new BufferedReader(isr);
        StringBuilder sb = new StringBuilder();
        String line;
        while((line = br.readLine()) != null){
            if(line.startsWith("app.version=")){
                line = "app.version="+version;
            }else if(line.startsWith("app.mainjar=")){
                line = "app.mainjar=.\\"+jarfile.getName();
            }
            sb.append(line).append('\n');
        }
        sb.deleteCharAt(sb.length()-1);
        br.close();
        isr.close();
        in.close();
        FileOutputStream os = new FileOutputStream(file);
        OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8");
        BufferedWriter bw = new BufferedWriter(osw);
        PrintWriter pw = new PrintWriter(bw);
        pw.print(sb);
        pw.flush();
        pw.close();
        bw.close();
        osw.close();
        os.close();
    }
    
    public static String installNPK(NPKItem item, NPKOriginal npk){
        if(item.getEnableFile().exists() || item.getDisableFile().exists()){
            String oldMD5 = FileUtil.getMD5(new FileOriginal(item.getEnableFile().exists()?item.getEnableFile():item.getDisableFile()));
            String newMD5 = FileUtil.getMD5(npk);
            if(Objects.equals(oldMD5, newMD5)){
                return "一 已存在同名且相同文件, 不进行替换";
            }else{
                if(item.deleteFile()){
                    try {
                        writeFile(npk, item.getEnableFile());
                        return "√ 存在同名但不相同文件, 已替换补丁";
                    } catch (Exception ex) {
                        System.out.println(ex);
                        return "× 存在同名但不相同文件, 已删除旧文件, 复制新文件失败";
                    }
                }else{
                    return "× 存在同名但不相同文件, 旧文件删除失败";
                }
            }
        }else{
            try {
                if(writeFile(npk, item.getEnableFile())){
                    return "√ 补丁添加成功";
                }
            } catch (Exception ex) {
                System.out.println(ex);
            }
            return "× 复制文件失败";
        }
    }
    
    public static boolean writeFile(NPKOriginal npk, File file) throws FileNotFoundException, IOException{
        FileOutputStream fos;
        switch(npk.getType()){
            case FILE:
                Files.copy(((FileOriginal)npk).getOriginal().toPath(), file.toPath());
                break;
            case BYTE_DOUBLE_ARRAY:
                fos = new FileOutputStream(file);
                for(byte[] d:((ByteWArrayOriginal)npk).getOriginal()){
                    fos.write(d);
                    fos.flush();
                }
                fos.close();
                break;
            case INPUT_STREAM:
                InputStream is = ((InputStreamOriginal)npk).getOriginal();
                fos = new FileOutputStream(file);
                byte[] b = new byte[1024];
                int length;
                while((length = is.read(b))>0){
                    fos.write(b,0,length);
                }
                fos.close();
                is.close();
                break;
        }
        return file.exists();
    }
    
    /**
     * 获取NPK的MD5码
     * @param npk NPK来源
     * @return MD5字符串
     */
    public static String getMD5(NPKOriginal npk) {
        MessageDigest digest = null;
        InputStream in = null;
        byte[] buffer = new byte[8192];
        int len;
        try {
            digest = MessageDigest.getInstance("MD5");
            switch(npk.getType()){
                case BYTE_DOUBLE_ARRAY:
                    for(byte[] d:((ByteWArrayOriginal)npk).getOriginal()){
                        digest.update(d, 0, d.length);
                    }
                    break;
                case INPUT_STREAM:
                    in = ((InputStreamOriginal)npk).getOriginal();
                    while ((len = in.read(buffer)) != -1) {
                        digest.update(buffer, 0, len);
                    }
                    in.close();
                    break;
                case FILE:
                    File file = ((FileOriginal)npk).getOriginal();
                    if (!file.isFile()) {
                        return null;
                    }
                    in = new FileInputStream(file);
                    while ((len = in.read(buffer)) != -1) {
                        digest.update(buffer, 0, len);
                    }
                    in.close();
                    break;
            }
            BigInteger bigInt = new BigInteger(1, digest.digest());
            return bigInt.toString(16);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
}
